#include <iostream>
#include <vector>
#include <assert.h>
#include "benchmark/benchmark.h"

// TODO NO CLASS
template <typename T, std::size_t length> static void bench_add_vec_no_class(benchmark::State &state)
{
    while (state.KeepRunning())
    {
        for (auto i = 0; i < length; i++)
        {
            std::vector<T> v0 = {1, 1, 1};
            std::vector<T> v1 = {2, 2, 2};
            std::vector<T> v2 = {3, 3, 3};
            std::vector<T> v3 = {4, 5, 6};
            std::vector<T> sum(3);
            for (size_t i = 0; i < 3; i++)
            {
                sum[i] = v0[i] + v1[i] + v2[i] + v3[i];
            }
        }
    }
}

// TODO class no inherit
template <typename T> class VecDirect
{
    std::vector<T> elems;

  public:
    T operator[](size_t i) const
    {
        return elems[i];
    }
    T &operator[](size_t i)
    {
        return elems[i];
    }
    size_t size() const
    {
        return elems.size();
    }

    VecDirect(size_t n) : elems(n)
    {
    }
    VecDirect(std::initializer_list<T> init) : elems(init)
    {
    }
};
template <typename T, std::size_t length> static void bench_add_vec_direct(benchmark::State &state)
{
    while (state.KeepRunning())
    {
        for (auto i = 0; i < length; i++)
        {
            VecDirect<T> v0 = {1, 1, 1};
            VecDirect<T> v1 = {2, 2, 2};
            VecDirect<T> v2 = {3, 3, 3};
            VecDirect<T> v3 = {4, 5, 6};

            VecDirect<T> sum(v0.size());
            for (size_t i = 0; i < sum.size(); i++)
            {
                sum[i] = v0[i] + v1[i] + v2[i] + v3[i];
            }
        }
    }
}

// TODO Inherit
template <typename T> class BaseVec
{
  public:
    virtual T      operator[](size_t i) const = 0;
    virtual size_t size() const               = 0;
    virtual ~BaseVec()
    {
    }
};

template <typename T> class VecInherit : public BaseVec<T>
{
    std::vector<T> elems;

  public:
    T operator[](size_t i) const override
    {
        return elems[i];
    }
    size_t size() const override
    {
        return elems.size();
    }

    VecInherit(size_t n) : elems(n)
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    VecInherit(std::initializer_list<T> init) : elems(init)
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    VecInherit &operator+=(const VecInherit &__d)
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
        assert(size() == __d.size());
        for (size_t i = 0; i < size(); ++i)
        {
            elems[i] += __d.elems[i];
        }
        return *this;
    }

    friend VecInherit operator+(VecInherit __x, const VecInherit &__y)
    // 注意在模板类外部进行定义时，返回值以及形参的类型都需要为 complex<T>，而不能是 complex
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
        assert(__x.size() == __y.size());
        // 这里创建新对象，性能消耗点
        VecInherit<T> r(__x.size());
        __x += __y;
        return __x;
    }

    void dump()
    {
        for (auto &elem : elems)
        {
            std::cout << elem << " ";
        }
        std::cout << std::endl;
    }
};

template <typename T, std::size_t length> static void bench_add_vec_inherit(benchmark::State &state)
{
    while (state.KeepRunning())
    {
        for (auto i = 0; i < length; i++)
        {
            VecInherit<T> v0  = {1, 1, 1};
            VecInherit<T> v1  = {2, 2, 2};
            VecInherit<T> v2  = {3, 3, 3};
            VecInherit<T> v3  = {4, 5, 6};
            VecInherit<T> sum = v0 + v1 + v2 + v3;
        }
    }
}

// TODO CRTP

// interface base class
template <typename T> class VecExpression
{
  public:
    double operator[](size_t i) const
    {
        return static_cast<T const &>(*this)[i];
    }
    size_t size() const
    {
        return static_cast<T const &>(*this).size();
    }
};

// 向量类
template <typename T> class Vec : public VecExpression<Vec<T>>
{
    std::vector<T> elems;

  public:
    T operator[](size_t i) const
    {
        return elems[i];
    }
    T &operator[](size_t i)
    {
        return elems[i];
    }
    size_t size() const
    {
        return elems.size();
    }

    Vec(size_t n) : elems(n)
    {
    }
    Vec(std::initializer_list<T> init) : elems(init)
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    // 从VecExpression构建Vec, 此时对表达式求值
    template <typename E> Vec(VecExpression<E> const &expr) : elems(expr.size())
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
        for (size_t i = 0; i != expr.size(); i++)
        {
            elems[i] = expr[i];
        }
    }

    void dump()
    {
        for (auto &elem : elems)
        {
            std::cout << elem << " ";
        }
        std::cout << std::endl;
    }
};

template <typename E1, typename E2> class VecSum : public VecExpression<VecSum<E1, E2>>
{
    E1 const &_u;
    E2 const &_v;

  public:
    VecSum(E1 const &u, E2 const &v) : _u(u), _v(v)
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
        assert(u.size() == v.size());
    }
    // Vec的构造函数中被调用，此时求值
    double operator[](size_t i) const
    {
        // std::cout << __PRETTY_FUNCTION__ << std::endl;
        return _u[i] + _v[i];
    }
    size_t size() const
    {
        return _v.size();
    }
};

// 将VecSum的构造函数封装成+运算符
template <typename E1, typename E2> VecSum<E1, E2> operator+(VecExpression<E1> const &u, VecExpression<E2> const &v)
{
    // std::cout << __PRETTY_FUNCTION__ << std::endl;
    return VecSum<E1, E2>(*static_cast<const E1 *>(&u), *static_cast<const E2 *>(&v));
}

template <typename T, std::size_t length> static void bench_add_vec_crtp(benchmark::State &state)
{
    while (state.KeepRunning())
    {
        for (auto i = 0; i < 100; i++)
        {
            Vec<T> v0 = {1, 1, 1};
            Vec<T> v1 = {2, 2, 2};
            Vec<T> v2 = {3, 3, 3};
            Vec<T> v3 = {4, 5, 6};

            Vec<T> sum = v0 + v1 + v2 + v3;
        }
    }
}

#define BENCHMARK_ALL(T, LENGTH)                           \
    BENCHMARK_TEMPLATE(bench_add_vec_no_class, T, LENGTH); \
    BENCHMARK_TEMPLATE(bench_add_vec_direct, T, LENGTH);   \
    BENCHMARK_TEMPLATE(bench_add_vec_crtp, T, LENGTH);     \
    BENCHMARK_TEMPLATE(bench_add_vec_inherit, T, LENGTH);

#define LENGTH 1000
BENCHMARK_ALL(char, LENGTH);
BENCHMARK_ALL(int, LENGTH);
BENCHMARK_ALL(float, LENGTH);
BENCHMARK_ALL(double, LENGTH);

BENCHMARK_MAIN();

// 2023-06-13T15:30:46+08:00
// Running ./express_templete_benchmark
// Run on (12 X 4500 MHz CPU s)
// CPU Caches:
//   L1 Data 32 KiB (x6)
//   L1 Instruction 32 KiB (x6)
//   L2 Unified 256 KiB (x6)
//   L3 Unified 12288 KiB (x1)
// Load Average: 0.93, 0.87, 0.95
// ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
// ------------------------------------------------------------------------------
// Benchmark                                    Time             CPU   Iterations
// ------------------------------------------------------------------------------
// bench_add_vec_no_class<char, 100>        59458 ns        59458 ns        11742
// bench_add_vec_direct<char, 100>          68237 ns        68237 ns         9112
// bench_add_vec_crtp<char, 100>            76024 ns        76023 ns         9032
// bench_add_vec_inherit<char, 100>        123236 ns       123236 ns         5583
// bench_add_vec_no_class<int, 100>         68127 ns        68127 ns        10129
// bench_add_vec_direct<int, 100>           75140 ns        75139 ns         8867
// bench_add_vec_crtp<int, 100>             89084 ns        89083 ns         7695
// bench_add_vec_inherit<int, 100>         144563 ns       144561 ns         4673
// bench_add_vec_no_class<float, 100>       65762 ns        65761 ns        10228
// bench_add_vec_direct<float, 100>         73716 ns        73714 ns         9132
// bench_add_vec_crtp<float, 100>           82646 ns        82645 ns         8019
// bench_add_vec_inherit<float, 100>       140563 ns       140557 ns         4979
// bench_add_vec_no_class<double, 100>      66102 ns        66101 ns        10211
// bench_add_vec_direct<double, 100>        74755 ns        74756 ns         8999
// bench_add_vec_crtp<double, 100>          82097 ns        82097 ns         8176
// bench_add_vec_inherit<double, 100>      139537 ns       139535 ns         4940

//  int main()
//  {

//      VecInherit<double> v00  = {1, 1, 1};
//      VecInherit<double> v01  = {2, 2, 2};
//      VecInherit<double> v02  = {3, 3, 3};
//      VecInherit<double> v03  = {4, 5, 6};
//      VecInherit<double> sum0 = v00 + v01 + v02 + v03;
//      std::cout << "--------------------------------------------------------------------" << std::endl;

//      Vec<double> v0 = {1, 1, 1};
//      Vec<double> v1 = {2, 2, 2};
//      Vec<double> v2 = {3, 3, 3};
//      Vec<double> v3 = {4, 5, 6};

//      Vec<double> sum = v0 + v1 + v2 + v3;
//      //  v0 + v1 =>      VecSum<Vec<double>, Vec<double> >
//      //  v0 + v2 + v3 => VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >
//      //  v0 + v2 + v3 + v4 => VecSum<VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >, Vec<double> >
//      //  只展开，没有计算
//      std::cout << "cal sum" << std::endl;

//      sum.dump(); // 计算
//      return 0;
//  }

// VecInherit<T>::VecInherit(std::initializer_list<_Tp>) [with T = double]
// VecInherit<T>::VecInherit(std::initializer_list<_Tp>) [with T = double]
// VecInherit<T>::VecInherit(std::initializer_list<_Tp>) [with T = double]
// VecInherit<T>::VecInherit(std::initializer_list<_Tp>) [with T = double]
// VecInherit<double> operator+(VecInherit<double>, const VecInherit<double>&)
// VecInherit<T>::VecInherit(size_t) [with T = double; size_t = long unsigned int]
// VecInherit<T>& VecInherit<T>::operator+=(const VecInherit<T>&) [with T = double]
// VecInherit<double> operator+(VecInherit<double>, const VecInherit<double>&)
// VecInherit<T>::VecInherit(size_t) [with T = double; size_t = long unsigned int]
// VecInherit<T>& VecInherit<T>::operator+=(const VecInherit<T>&) [with T = double]
// VecInherit<double> operator+(VecInherit<double>, const VecInherit<double>&)
// VecInherit<T>::VecInherit(size_t) [with T = double; size_t = long unsigned int]
// VecInherit<T>& VecInherit<T>::operator+=(const VecInherit<T>&) [with T = double]
// --------------------------------------------------------------------
// Vec<T>::Vec(std::initializer_list<_Tp>) [with T = double]
// Vec<T>::Vec(std::initializer_list<_Tp>) [with T = double]
// Vec<T>::Vec(std::initializer_list<_Tp>) [with T = double]
// Vec<T>::Vec(std::initializer_list<_Tp>) [with T = double]
// VecSum<E1, E2> operator+(const VecExpression<E1>&, const VecExpression<E2>&) [with E1 = Vec<double>; E2 = Vec<double>]
// VecSum<E1, E2>::VecSum(const E1&, const E2&) [with E1 = Vec<double>; E2 = Vec<double>]
// VecSum<E1, E2> operator+(const VecExpression<E1>&, const VecExpression<E2>&) [with E1 = VecSum<Vec<double>, Vec<double> >; E2 = Vec<double>]
// VecSum<E1, E2>::VecSum(const E1&, const E2&) [with E1 = VecSum<Vec<double>, Vec<double> >; E2 = Vec<double>]
// VecSum<E1, E2> operator+(const VecExpression<E1>&, const VecExpression<E2>&) [with E1 = VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >; E2 = Vec<double>]
// VecSum<E1, E2>::VecSum(const E1&, const E2&) [with E1 = VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >; E2 = Vec<double>]
// Vec<T>::Vec(const VecExpression<E>&) [with E = VecSum<VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >, Vec<double> >; T = double]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<Vec<double>, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = Vec<double>; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<Vec<double>, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = Vec<double>; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<VecSum<Vec<double>, Vec<double> >, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = VecSum<Vec<double>, Vec<double> >; E2 = Vec<double>; size_t = long unsigned int]
// double VecSum<E1, E2>::operator[](size_t) const [with E1 = Vec<double>; E2 = Vec<double>; size_t = long unsigned int]
// cal sum
// 10 11 12